package com.jcfk.eam.service.mt;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jcfk.common.business.service.BaseService;
import com.jcfk.common.core.utils.ExcelUtils;
import com.jcfk.common.web.domain.RetResult;
import com.jcfk.eam.api.SysServiceFeign;
import com.jcfk.eam.dao.main.EamAssetInfoDao;
import com.jcfk.eam.domain.dto.base.EamAssetTypeDTO;
import com.jcfk.eam.domain.dto.base.EamInspectionRouteLocationDTO;
import com.jcfk.eam.domain.dto.base.EamLocationInfoDTO;
import com.jcfk.eam.domain.dto.base.EamMakerDTO;
import com.jcfk.eam.domain.dto.main.EamAssetInfoDTO;
import com.jcfk.eam.domain.po.main.EamAssetInfo;
import com.jcfk.eam.domain.query.base.EamAssetTypeQuery;
import com.jcfk.eam.domain.query.base.EamLocationInfoQuery;
import com.jcfk.eam.domain.query.base.EamMakerQuery;
import com.jcfk.eam.domain.uploadExcel.main.EamAssetInfoUploadExcel;
import com.jcfk.eam.domain.uploadExcel.mt.MtEamAssetInfoUploadExcel;
import com.jcfk.eam.enums.AssetKind;
import com.jcfk.eam.service.base.EamAssetTypeService;
import com.jcfk.eam.service.base.EamLocationInfoService;
import com.jcfk.eam.service.base.EamMakerService;
import com.jcfk.eam.service.main.EamAssetInfoService;
import com.jcfk.system.api.ifeign.SysDictionaryServiceFeign;
import com.jcfk.system.api.utils.DictionaryUtils;
import com.jcfk.system.domain.dto.SysDictionaryItemDTO;
import com.jcfk.system.domain.dto.SysOrganizationDTO;
import com.jcfk.system.domain.dto.SysUserDTO;
import com.jcfk.system.domain.query.SysOrganizationQuery;
import com.jcfk.system.domain.query.SysUserQuery;
import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static com.jcfk.common.core.utils.BeanUtils.convertList;

/**
 * 量检具台账
 *
 * @author libinghai
 * @email 35738025@gqq.com
 * @date 2024-07-28
 */
@Service
public class MtAssetInfoService extends BaseService<EamAssetInfoDao, EamAssetInfo> {

    @Resource
    private EamAssetTypeService eamAssetTypeService;

    @Resource
    private EamAssetInfoService eamAssetInfoService;

    @Resource
    private SysServiceFeign sysServiceFeign;

    @Resource
    private EamLocationInfoService eamLocationInfoService;

    @Resource
    private EamMakerService eamMakerService;

    @Resource
    private DictionaryUtils dictionaryUtils;
    /**
     * 验证量检具信息有效性
     *
     * @param dto
     * @return
     */
    public RetResult<String> isValid(EamAssetInfoDTO dto) {
        Integer count=0;
        // 判断登录名
        QueryWrapper<EamAssetInfo> qw = new QueryWrapper<>();
        if (!StrUtil.isEmpty(dto.getId())) {
            qw.lambda().ne(EamAssetInfo::getId, dto.getId());
        }
        qw.lambda().eq(EamAssetInfo::getAssetCode, dto.getAssetCode());

        count = this.baseMapper.selectCount(qw);
        if (count > 0) {
            return RetResult.fail("量检具编号已存在");
        }

        return RetResult.ok();
    }

    @SneakyThrows
    public Map<String, Object> upload(List<MtEamAssetInfoUploadExcel> items) {
        Map<String, Object> result = MapUtil.<String, Object>builder()
            .put("status", "success")
            .put("data", null)
            .build();
        Map<Integer,String> errorMap = new HashMap<>();
        Map<Integer,String> warningMap = new HashMap<>();
        // 校验数据
        validateFileItem(items,errorMap,warningMap);

        BiFunction<Map.Entry<Integer,String>, String, Map<String, String>> buildMsg = (entry, type) -> MapUtil.<String, String>builder()
            .put("row", entry.getKey().toString())
            .put("type", type)
            .put("error", entry.getValue())
            .build();

        // 如果有异常数据  导入失败并返回
        if (CollUtil.isNotEmpty(errorMap.entrySet())) {
            return MapUtil.<String, Object>builder()
                .put("status", "error")
                .put("data", Stream.concat(
                    // 异常消息
                    errorMap.entrySet().stream().map(t -> buildMsg.apply(t, "error")),
                    // 警告消息
                    warningMap.entrySet().stream().map(t -> buildMsg.apply(t, "warning")))
                    .collect(Collectors.toList()))
                .build();
        }
        // 如果有警告数据  继续导入并返回警告信息
        if (CollUtil.isNotEmpty(warningMap.entrySet())) {
            result = MapUtil.<String, Object>builder()
                .put("status", "warning")
                .put("data", warningMap.entrySet().stream().map(t -> buildMsg.apply(t, "warning")).collect(Collectors.toList()))
                .build();
        }
        // 存在警告信息的数据移除(数组下标为行号-4)
        warningMap.keySet().stream()
            .sorted(Collections.reverseOrder())
            .forEach(index -> items.remove(index - 4));
        List<EamAssetInfo> eamAssetInfoList = items.stream()
            .map(item -> {
                EamAssetInfo eamAssetInfo = BeanUtil.copyProperties(item, EamAssetInfo.class, "usingDate");
                eamAssetInfo.setAssetKind(AssetKind.MEASURING_TOOL.getValue());
                if (StrUtil.isNotEmpty(item.getUsingDate()))
                    eamAssetInfo.setUsingDate(DateUtil.parse(item.getUsingDate(),"yyyy.MM"));
                if (item.getNextCheckDateMonthInt() != null && item.getNextCheckDateDayInt() != null)
                    eamAssetInfo.setNextCheckDate(DateUtil.beginOfYear(DateUtil.date())
                        .offset(DateField.MONTH, item.getNextCheckDateMonthInt() - 1)
                        .offset(DateField.DAY_OF_MONTH, item.getNextCheckDateDayInt() - 1));
                return eamAssetInfo;
            })
            .collect(Collectors.toList());
        eamAssetInfoService.insertOrUpdateBatch(eamAssetInfoList);
        return result;
    }

    private void validateFileItem(List<MtEamAssetInfoUploadExcel> items,Map<Integer,String> errorMap,Map<Integer,String> warningMap) {
        // 器具类型
        EamAssetTypeQuery assetTypeQuery = new EamAssetTypeQuery();
        assetTypeQuery.setEnabled(1);
        Map<String,EamAssetTypeDTO> assetTypeMap = eamAssetTypeService.getList(assetTypeQuery)
            .stream()
            .collect(Collectors.toMap(EamAssetTypeDTO::getAssetTypeName, Function.identity(), (v1, v2) -> v1));
        // 管理类别
        Map<String, String> equipmentCategoryMap = dictionaryUtils.getDictionaryItems("EquipmentCategory")
            .stream()
            .collect(Collectors.toMap(SysDictionaryItemDTO::getItemLabel, SysDictionaryItemDTO::getItemValue));
        // 使用部门
        SysOrganizationQuery organizationQuery = new SysOrganizationQuery();
        organizationQuery.setEnabled(1);
        Map<String, SysOrganizationDTO> organizationMap = sysServiceFeign.getOrganizationList(organizationQuery).getData()
            .stream()
            .collect(Collectors.toMap(SysOrganizationDTO::getOrganizationName, Function.identity(), (v1, v2) -> v1));
        // 使用者
        SysUserQuery userQuery = new SysUserQuery();
        userQuery.setEnabled(1);
        Map<String, SysUserDTO> userMap = sysServiceFeign.getUserList(userQuery).getData()
            .stream()
            .collect(Collectors.toMap(SysUserDTO::getFirstName, Function.identity(), (v1, v2) -> v1));
        // 位置
        EamLocationInfoQuery query = new EamLocationInfoQuery();
        query.setEnabled(1);
        Map<String, EamLocationInfoDTO> locationMap = eamLocationInfoService.getList(query)
            .stream()
            .collect(Collectors.toMap(EamLocationInfoDTO::getLocationName, Function.identity(), (v1, v2) -> v1));
        // 状态
        Map<String, String> assetStatusMap = dictionaryUtils.getDictionaryItems("mtAssetStatus")
            .stream()
            .collect(Collectors.toMap(SysDictionaryItemDTO::getItemLabel, SysDictionaryItemDTO::getItemValue));
        // 制造商
        EamMakerQuery makerQuery = new EamMakerQuery();
//        makerQuery.setEnabled(1);
        makerQuery.setDeleted(0);
        Map<String, EamMakerDTO> makerMap = eamMakerService.getList(makerQuery)
            .stream()
            .collect(Collectors.toMap(EamMakerDTO::getMakerName, Function.identity(), (v1, v2) -> v1));

        // 定义一个通用的 BiConsumer 来更新 Map
        BiConsumer<Map<Integer, String>, List<Object>> updateMap = (map, list) -> {
            Integer key = (Integer) CollUtil.getFirst(list);
            String errorMsg = CollUtil.getLast(list).toString();
            if (!map.containsKey(key)) {
                map.put(key, errorMsg);
            } else {
                map.put(key, map.get(key) + ";" + errorMsg);
            }
        };
        // 构建错误信息
        BiConsumer<String, Integer> buildErrorMsg = (errorMsg, key) -> updateMap.accept(errorMap, ListUtil.of(key, errorMsg));
        // 构建警告信息
        BiConsumer<String, Integer> buildWarningMap = (errorMsg, key) -> updateMap.accept(warningMap, ListUtil.of(key, errorMsg));

        BiFunction<String, String, Consumer<Integer>> checkSlashes = (value, label) -> (index) -> {
            if (StrUtil.isNotEmpty(value) && value.equals("///"))
                buildErrorMsg.accept(label + "不能为:\"///\"", index);
        };

        Map<String, String> excelExist = new HashMap<>();
        for (int i = 0; i < items.size(); i++) {
            MtEamAssetInfoUploadExcel item = items.get(i);
            // 行号
            int index = i + 4;
            if (excelExist.get(item.getEnterpriseCode() + item.getAssetName()) == null) {
                excelExist.put(item.getEnterpriseCode() + item.getAssetName(), item.getEnterpriseCode() + item.getAssetName());
            }else {
                buildErrorMsg.accept("企业编码和器具名称存在重复", index);
            }
            // 企业编码不能为空
            if (StrUtil.isEmpty(item.getEnterpriseCode())) {
                buildWarningMap.accept("企业编码不能为空", index);
            } else {
                item.setAssetCode(StrUtil.format("{}{}", DateUtil.format(DateUtil.date(), "yyyyMMddHHmm"), item.getEnterpriseCode()));
            }
            // 器具名称
            if (StrUtil.isEmpty(item.getAssetName())) {
                buildErrorMsg.accept("器具名称不能为空", index);
            }
            // 状态
            if (StrUtil.isEmpty(item.getAssetStatus())) {
                buildErrorMsg.accept("状态不能为空", index);
            }
            // 器具类型
            if (StrUtil.isEmpty(item.getAssetTypeName())) {
                buildErrorMsg.accept("器具类型不能为空", index);
            } else {
                EamAssetTypeDTO eamAssetType = assetTypeMap.get(item.getAssetTypeName());
                if (eamAssetType == null) {
                    buildErrorMsg.accept("器具类型不正确", index);
                } else {
                    item.setAssetTypeId(eamAssetType.getId());
                    item.setAssetTypeCode(eamAssetType.getAssetTypeCode());
                }
            }
            // 管理类别
            if (StrUtil.isNotEmpty(item.getMeasuringType())) {
                String measuringType = equipmentCategoryMap.get(item.getMeasuringType());
                if (measuringType == null) {
                    buildErrorMsg.accept("管理类别不正确", index);
                } else {
                    item.setMeasuringType(measuringType);
                }
            }
            // 使用部门
            if (StrUtil.isNotEmpty(item.getUsingDepartmentName())) {
                SysOrganizationDTO sysOrganizationDTO = organizationMap.get(item.getUsingDepartmentName());
                if (sysOrganizationDTO == null)
                    buildErrorMsg.accept("使用部门不正确", index);
                else {
                    item.setUsingDepartmentId(sysOrganizationDTO.getId());
                }
            }
            // 使用者
            if (StrUtil.isNotEmpty(item.getUsingPersonName())) {
                SysUserDTO userDTO = userMap.get(item.getUsingPersonName());
                if (userDTO == null)
                    buildErrorMsg.accept("使用者不正确", index);
                else {
                    SysOrganizationDTO sysOrganizationDTO = organizationMap.get(item.getUsingDepartmentName());
                    if (StrUtil.isNotEmpty(item.getUsingDepartmentName()) && sysOrganizationDTO != null && !sysOrganizationDTO.getId().equals(userDTO.getOrganizationId())) {
                        buildErrorMsg.accept("使用者不属于此属于部门", index);
                    } else {
                        item.setUsingPersonId(userDTO.getId());
                    }
                }
            }
            // 使用/保存地点
            if (StrUtil.isNotEmpty(item.getLocationName())) {
                EamLocationInfoDTO eamLocationInfoDTO = locationMap.get(item.getLocationName());
                if (eamLocationInfoDTO == null)
                    buildErrorMsg.accept("使用/保存地点不正确", index);
                else {
                    item.setLocationId(eamLocationInfoDTO.getId());
                }
            }
            //状态
            if (StrUtil.isNotEmpty(item.getAssetStatus())) {
                String assetStatus = assetStatusMap.get(item.getAssetStatus());
                if (assetStatus == null)
                    buildErrorMsg.accept("状态不正确", index);
                else {
                    item.setAssetStatus(assetStatus);
                }
            }
            //制造商
            if (StrUtil.isNotEmpty(item.getMakerName())) {
                EamMakerDTO maker = makerMap.get(item.getMakerName());
                if (maker == null)
                    buildErrorMsg.accept("制造商不正确", index);
                else {
                    item.setMakerId(maker.getId());
                    item.setMakerCode(maker.getMakerCode());
                }
            }
            try {
                if (StrUtil.isEmpty(item.getNextCheckDateDay()) && StrUtil.isNotEmpty(item.getNextCheckDateMonth())){
                    buildErrorMsg.accept("检定月存在,请输入到期日", index);
                    continue;
                }
                if (StrUtil.isNotEmpty(item.getNextCheckDateDay()) && StrUtil.isNotEmpty(item.getNextCheckDateMonth())){
                    int day = BigDecimal.valueOf(Double.parseDouble(item.getNextCheckDateDay())).intValue();
                    if (day < 1 || day > 31) buildErrorMsg.accept("到期日请输入1-31", index);
                    else item.setNextCheckDateDayInt(day);
                }
            }catch (NumberFormatException e){
                buildErrorMsg.accept("到期日不正确", index);
            }
            try {
                if (StrUtil.isNotEmpty(item.getNextCheckDateDay()) && StrUtil.isEmpty(item.getNextCheckDateMonth())){
                    buildErrorMsg.accept("到期日存在,请输入检定月", index);
                    continue;
                }
                if (StrUtil.isNotEmpty(item.getNextCheckDateDay()) && StrUtil.isNotEmpty(item.getNextCheckDateMonth())){
                    int month = BigDecimal.valueOf(Double.parseDouble(item.getNextCheckDateMonth())).intValue();
                    if (month < 1 || month > 12) buildErrorMsg.accept("检定月请输入1-12", index);
                    else item.setNextCheckDateMonthInt(month);
                }
            }catch (NumberFormatException e){
                buildErrorMsg.accept("检定月不正确", index);
            }
            // 校验字段不能为"///"
            checkSlashes.apply(item.getEnterpriseCode(), "企业编号").accept(index);
            checkSlashes.apply(item.getAssetName(), "器具名称").accept(index);
            checkSlashes.apply(item.getAssetTypeName(), "器具类型").accept(index);
            checkSlashes.apply(item.getAssetSpecification(), "规格/型号").accept(index);
            checkSlashes.apply(item.getDrawingNumber(), "检具图号").accept(index);
            checkSlashes.apply(item.getAccuracyLevel(), "准确度等级").accept(index);
            checkSlashes.apply(item.getMeasuringDivision(), "分度值/分辨力").accept(index);
            checkSlashes.apply(item.getMakerName(), "制造商").accept(index);
            checkSlashes.apply(item.getManufactureCode(), "出厂编号（表杆）").accept(index);
            checkSlashes.apply(item.getHeaderNumber(), "出厂编号（表头）").accept(index);
            checkSlashes.apply(item.getUsingDate(), "使用日期").accept(index);
            checkSlashes.apply(item.getMeasuringType(), "管理类别").accept(index);
            checkSlashes.apply(item.getCheckPeriod(), "检定周期").accept(index);
            checkSlashes.apply(item.getNextCheckDateMonth(), "检定月").accept(index);
            checkSlashes.apply(item.getNextCheckDateDay(), "到期日").accept(index);
            checkSlashes.apply(item.getUsingDepartmentName(), "使用部门").accept(index);
            checkSlashes.apply(item.getUsingPersonName(), "使用者").accept(index);
            checkSlashes.apply(item.getLocationName(), "使用/存放地点").accept(index);
            checkSlashes.apply(item.getAssetStatus(), "状态").accept(index);
            checkSlashes.apply(item.getCheckPlace(), "检定地点").accept(index);
            checkSlashes.apply(item.getCheckPerson(), "检定员").accept(index);
            checkSlashes.apply(item.getAssetForm(), "资产形式").accept(index);
            checkSlashes.apply(item.getAssetAttribute1(), "属性").accept(index);
            checkSlashes.apply(item.getOrderNumber(), "订货号").accept(index);
            checkSlashes.apply(item.getRemarks(), "备注").accept(index);

            if (StrUtil.isNotEmpty(item.getAssetForm()) && item.getAssetForm().equals("固定资产"))
                item.setIsFixedAsset(1);
        }
    }
}
